// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:convert';
import 'dart:io' as io;

import 'package:args/command_runner.dart';
import 'package:path/path.dart' as path;

import 'environment.dart';
import 'felt_config.dart';
import 'package_lock.dart';

class GenerateBuilderJsonCommand extends Command<bool> {
  @override
  String get description =>
      'Generates JSON for the engine_v2 builders to build and copy all artifacts, '
      'compile all test bundles, and run all test suites on all platforms.';

  @override
  String get name => 'generate-builder-json';

  @override
  FutureOr<bool>? run() {
    final PackageLock packageLock = PackageLock();
    final FeltConfig config = FeltConfig.fromFile(
      path.join(environment.webUiTestDir.path, 'felt_config.yaml'),
    );
    _writeBuilderJson(
      _generateBuilderJson(
        config.testBundles.map((TestBundle bundle) => _getBundleBuildStep(bundle)).toList(),
        _getAllTestSteps(config.testSuites, packageLock),
      ),
      'linux_web_engine_test.json',
    );
    return true;
  }

  void _writeBuilderJson(String builderConfig, String filename) {
    final io.File buildConfigFile = io.File(
      path.join(environment.flutterDirectory.path, 'ci', 'builders', filename),
    );
    buildConfigFile.createSync(recursive: true);
    buildConfigFile.writeAsStringSync('$builderConfig\n');
  }

  String _generateBuilderJson(
    Iterable<Map<String, dynamic>> builds,
    Iterable<Map<String, dynamic>> tests,
  ) {
    final Map<String, dynamic> outputJson = <String, dynamic>{
      '_comment': 'THIS IS A GENERATED FILE. Do not edit this file directly.',
      '_comment2': 'See `generate_builder_json.dart` for the generator code',
      'builds': builds,
      'tests': tests,
    };
    return const JsonEncoder.withIndent('  ').convert(outputJson);
  }

  Map<String, dynamic> _getBundleBuildStep(TestBundle bundle) {
    return <String, dynamic>{
      'name': 'web_tests/test_bundles/${bundle.name}',
      'drone_dimensions': <String>['device_type=none', 'os=Linux'],
      'generators': <String, dynamic>{
        'tasks': <dynamic>[
          <String, dynamic>{
            'name': 'compile bundle ${bundle.name}',
            'parameters': <String>['test', '--compile', '--bundle=${bundle.name}'],
            'scripts': <String>['flutter/lib/web_ui/dev/felt'],
          },
        ],
      },
    };
  }

  Map<String, dynamic> _getAnalysisAndLicenseStep() {
    return <String, dynamic>{
      'name': 'web engine analysis and license checks',
      'recipe': 'engine_v2/tester_engine',
      'drone_dimensions': <String>['device_type=none', 'os=Linux'],
      'tasks': <dynamic>[
        <String, dynamic>{
          'name': 'check licenses',
          'parameters': <String>['check-licenses'],
          'script': 'flutter/lib/web_ui/dev/felt',
        },
      ],
    };
  }

  Iterable<Map<String, dynamic>> _getAllTestSteps(List<TestSuite> suites, PackageLock packageLock) {
    return <Map<String, dynamic>>[
      _getAnalysisAndLicenseStep(),
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.chrome),
      _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.firefox),
      _getTestStepForPlatformAndBrowser(
        suites,
        packageLock,
        'Mac',
        BrowserName.safari,
        specificOS: 'Mac-14|Mac-15',
        cpu: 'arm64',
      ),
    ];
  }

  Map<String, dynamic> _getTestStepForPlatformAndBrowser(
    List<TestSuite> suites,
    PackageLock packageLock,
    String platform,
    BrowserName browser, {
    String? specificOS,
    String? cpu,
  }) {
    final filteredSuites = suites.where(
      (suite) => suite.enableCi && suite.runConfig.browser == browser,
    );
    final bundles = filteredSuites.map((suite) => suite.testBundle).toSet();
    return <String, dynamic>{
      'name': '$platform run ${browser.name} suites',
      'recipe': 'engine_v2/tester_engine',
      'drone_dimensions': <String>[
        'device_type=none',
        'os=${specificOS ?? platform}',
        if (cpu != null) 'cpu=$cpu',
      ],
      'gclient_variables': <String, dynamic>{'download_android_deps': false, 'download_jdk': false},
      'dependencies': <String>[...bundles.map((bundle) => 'web_tests/test_bundles/${bundle.name}')],
      'test_dependencies': <dynamic>[
        <String, dynamic>{
          'dependency': 'goldctl',
          'version': 'git_revision:720a542f6fe4f92922c3b8f0fdcc4d2ac6bb83cd',
        },
        if (browser == BrowserName.chrome)
          <String, dynamic>{
            'dependency': 'chrome_and_driver',
            'version': packageLock.chromeLock.version,
          },
        if (browser == BrowserName.firefox)
          <String, dynamic>{
            'dependency': 'firefox',
            'version': 'version:${packageLock.firefoxLock.version}',
          },
      ],
      'tasks': [
        <String, dynamic>{
          'name': 'copy artifacts',
          'parameters': <String>[
            'test',
            '--copy-artifacts',
            for (final TestSuite suite in filteredSuites) '--suite=${suite.name}',
          ],
          'script': 'flutter/lib/web_ui/dev/felt',
        },
        ...filteredSuites.map(
          (suite) => <String, dynamic>{
            'name': 'run suite ${suite.name}',
            'parameters': <String>['test', '--run', '--suite=${suite.name}'],
            'script': 'flutter/lib/web_ui/dev/felt',
          },
        ),
      ],
    };
  }
}
